package com.mjlf.framework.mydata;

import com.mjlf.framework.annotation.mydata.Param;
import com.mjlf.framework.annotation.mydata.Sql;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.collections.map.HashedMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class MapperProxy {

    public static <T> T createProxy(final Class<T> targetClass) {
        return (T) Enhancer.create(targetClass, new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.isAnnotationPresent(Sql.class)) {

                    //兑换sql语句
                    String sql = changeSql(method, objects);
                    Connection connection = null;
                    try {
                        connection = ConnectionManager.getConnection();
                        PreparedStatement statement = connection.prepareStatement(sql);
                        if (sql.toUpperCase().startsWith("INSERT") ||
                                sql.toUpperCase().startsWith("DELETE") ||
                                sql.toUpperCase().startsWith("UPDATE")) {
                            return statement.executeUpdate();
                        } else {
                            ResultSet resultSet = statement.executeQuery();
                            Type type = method.getGenericReturnType();
                            return setResult(resultSet, type);
                        }
                    } finally {
                        if (connection != null) {
                            ConnectionManager.retriveConnection(connection);
                        }
                    }
                } else {
                    return methodProxy.invokeSuper(o, objects);
                }
            }
        });
    }

   public static Object createMapperProxy(Method method, Object[] objects, MethodProxy methodProxy, Object o) throws Throwable {

       Object result = null;
       //执行数据库操作代理
       if (method.isAnnotationPresent(Sql.class)) {

           //兑换sql语句
           String sql = MapperProxy.changeSql(method, objects);
           Connection connection = null;
           try {
               connection = ConnectionManager.getConnection();
               PreparedStatement statement = connection.prepareStatement(sql);
               if (sql.toUpperCase().startsWith("INSERT") ||
                       sql.toUpperCase().startsWith("DELETE") ||
                       sql.toUpperCase().startsWith("UPDATE")) {
                   result = statement.executeUpdate();
               } else {
                   ResultSet resultSet = statement.executeQuery();
                   Type type = method.getGenericReturnType();
                   result = MapperProxy.setResult(resultSet, type);
               }
           } finally {
               if (connection != null) {
                   ConnectionManager.retriveConnection(connection);
               }
           }
       } else {
           result = methodProxy.invokeSuper(o, objects);
       }
       return result;
   }

    /**
     * 从resultSet中获取执行结果并返回
     *
     * @param resultSet
     * @param returnType
     * @param
     *
     * @return
     *
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static Object setResult(ResultSet resultSet, Type returnType) throws Exception {
        ResultSetMetaData metaData = resultSet.getMetaData();

        //每个结果中所带的属性数
        int columnSize = metaData.getColumnCount();

        //获取结果名称
        Map<String, Integer> resultNamesMap = new HashedMap();
        for (int i = 1; i <= columnSize; i++) {
            String columnLabel = metaData.getColumnLabel(i);
            Integer columnType = metaData.getColumnType(i);

            resultNamesMap.put(columnLabel, columnType);
        }

        //mysql返回结果中的字段名称
        Set<String> columnLabelKeys = resultNamesMap.keySet();

        //方法要求返回结果类型字符串
        String returnTypeStr = returnType.getTypeName();

        boolean isCollection = false;
        if (returnTypeStr.contains("java.util.List") || returnTypeStr.contains("java.util.Set")) {
            isCollection = true;
            Collection collection = null;
            if (returnTypeStr.contains("java.util.List")) {
                collection = new ArrayList();
            } else {
                collection = new HashSet();
            }
            String[] resultTypeStrs = returnTypeStr.split("[<>]");
            if (resultTypeStrs.length > 1) {
                //返回结果对象，获取对象中的set方法
               setAttar(resultTypeStrs[1], resultSet, resultNamesMap, collection);
            } else {
                while (resultSet.next()) {
                    for (String columnLabel : columnLabelKeys) {
                        collection.add(resultSet.getObject(columnLabel));
                    }
                }
            }
            return collection;
        }
        return setAttar(returnType.getTypeName(), resultSet, resultNamesMap, null);
    }

    public static Object setAttar(String resultType, ResultSet resultSet, Map<String, Integer> resNamesMap, Collection collection) throws Exception {

        //mysql返回字段名称集合
        Set<String> columnLabelKeys = resNamesMap.keySet();


        Object res = Class.forName(resultType).newInstance();
        Map<String, Method> resMethodMap = new HashedMap();
        Method[] methods = res.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (!method.getName().startsWith("set")) {
                continue;
            }
            if (method.getParameterTypes().length != 1) {
                continue;
            }
            String name = method.getName();
            name = name.replace("set", "");
            String first = name.substring(0, 1).toLowerCase();
            name = name.replaceFirst("\\w{1}", first);
            resMethodMap.put(name, method);
        }

        //向对象中设置属性
        int index = 1;
        while (resultSet.next()) {
            if(collection == null && index > 1){
                throw new Exception("对应希望找到一个， 可是找到了多条对应的数据");
            }
            Object indexResult = Class.forName(resultType).newInstance();
            for (String columnLabel : columnLabelKeys) {
                Integer columnType = resNamesMap.get(columnLabel);
                if (resMethodMap.containsKey(columnLabel)) {
                    Method attarMethod = resMethodMap.get(columnLabel);
                    switch (columnType) {
                        case Types.INTEGER:
                            attarMethod.invoke(indexResult, resultSet.getInt(columnLabel));
                            break;
                        case Types.BIGINT:
                            attarMethod.invoke(indexResult, resultSet.getLong(columnLabel));
                            break;
                        case Types.BLOB:
                        case Types.CHAR:
                        case Types.VARCHAR:
                            attarMethod.invoke(indexResult, resultSet.getString(columnLabel));
                            break;
                        case Types.DOUBLE:
                        case Types.DECIMAL:
                            attarMethod.invoke(indexResult, resultSet.getDouble(columnLabel));
                            break;
                        case Types.DATE:
                            attarMethod.invoke(indexResult, resultSet.getDate(columnLabel));
                            break;
                    }
                }
            }
            if(collection != null){
                collection.add(indexResult);
            }
            res = indexResult;
        }
        if(collection != null){
            return collection;
        }else {
            return res;
        }
    }

    public static String changeSql(Method method, Object[] objects) throws Exception {
        String reg = "#\\{\\w+\\}";
        Pattern pattern = Pattern.compile(reg);
        String sql = null;
        if (method.isAnnotationPresent(Sql.class)) {
            Sql sqlAnn = method.getAnnotation(Sql.class);
            sql = sqlAnn.value();
        } else {
            throw new Exception("该方法不是sql方法");
        }
        Matcher matcher = pattern.matcher(sql);
        int i = 0;

        Map<String, Object> paramsMap = new HashMap<>();
        Parameter[] parameters = method.getParameters();
        for (int index = 0; index < parameters.length; index++) {
            Parameter parameter = parameters[index];
            if (parameter.isAnnotationPresent(Param.class)) {
                Param param = parameter.getAnnotation(Param.class);
                paramsMap.put(param.value(), objects[index]);
            } else {
                Object obj = objects[index];
                Method[] paramMethods = obj.getClass().getDeclaredMethods();
                for (Method m : paramMethods) {
                    if (m.getName().startsWith("get") && m.getParameters().length == 0) {
                        String name = m.getName().substring(3);
                        String first = name.substring(0, 1);
                        first = first.toLowerCase();
                        name = name.replaceFirst("\\w{1}", first);
                        try {
                            paramsMap.put(name, m.invoke(obj, null));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        while (matcher.find()) {
            String find = matcher.group(0);
            String parameter = find.substring(2, find.lastIndexOf("}"));
            if (paramsMap.containsKey(parameter)) {
                Object p = paramsMap.get(parameter);
                if (p instanceof String) {
                    sql = sql.replaceFirst(reg, "'" + p.toString() + "'");
                } else {
                    sql = sql.replaceFirst(reg, p.toString());
                }
            } else {
                throw new Exception("没有这样的参数");
            }
        }

        return sql;
    }
}